package com.wangl.generator.wlmybatisgenerator.ui;

import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.wangl.generator.wlmybatisgenerator.data.ConnectionBean;
import com.wangl.generator.wlmybatisgenerator.data.DataStore;
import com.wangl.generator.wlmybatisgenerator.generator.GeneratorService;
import com.wangl.generator.wlmybatisgenerator.generator.MyBatisGeneratorConfig;
import com.wangl.generator.wlmybatisgenerator.listener.AutoCompleter;
import com.wangl.generator.wlmybatisgenerator.persistent.ConnectionPersistent;
import com.wangl.generator.wlmybatisgenerator.utils.JdbcUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import java.awt.event.*;
import java.io.File;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public class MybatisGeneratorDialog extends JDialog {
    private JPanel contentPane;
    private JButton buttonOK;
    private JButton buttonCancel;
    private JComboBox<String> dbSelect;
    private JTextField entityPackage;
    private JTextField daoPackage;
    private JTextField xmlPackage;
    private JRadioButton lombokRadio;
    private JTextField host;
    private JTextField port;
    private JTextField username;
    private JTextField password;
    private JButton connectBtn;
    private JButton entityFileBtn;
    private JButton daoFileBtn;
    private JButton mapperFileBtn;
    private JPanel connectionsPanel;
    private JList<String> connectionList;
    private JTextField connectionName;
    private JPanel formPanel;
    private JRadioButton mergeRadio;
    private JTable selectTable;
    private JScrollPane tablePane;
    private JTextField tablePrefix;
    private JTextField tableSearch;
    private JRadioButton tinyintCastInteger;

    private Map<String,ConnectionBean> cache;

    public MybatisGeneratorDialog() {
        setContentPane(contentPane);
        setModal(true);
        getRootPane().setDefaultButton(buttonOK);
        dbSelect.setEditable(true);

        //连接列表持久化
        ConnectionPersistent instance = ConnectionPersistent.getInstance(DataStore.project);
        List<ConnectionBean> connections = instance.getConnections();
        cache = connections.stream().collect(Collectors.toMap(ConnectionBean::getConnectionName, Function.identity()));
        DefaultComboBoxModel<String> listModel = new DefaultComboBoxModel<>(connections.stream().map(ConnectionBean::getConnectionName).toArray(String[]::new));
        connectionList.setModel(listModel);
        // 列表添加选中事件
        connectionList.addListSelectionListener(e -> {
            String selectedValue = connectionList.getSelectedValue();
            ConnectionBean connectionBean = cache.get(selectedValue);
            if (connectionBean != null){
                setConnection(connectionBean);
            }
        });

        JPopupMenu popupMenu = new JPopupMenu();
        JMenuItem deleteItem = new JMenuItem("delete");
        popupMenu.add(deleteItem);
        deleteItem.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                String selectedValue = connectionList.getSelectedValue();
                ConnectionBean remove = cache.remove(selectedValue);
                connections.remove(remove);
                listModel.removeElement(selectedValue);
                clearForm();
            }
        });

        connectionList.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                connectionList.setSelectedIndex(connectionList.locationToIndex(e.getPoint()));
                if (SwingUtilities.isRightMouseButton(e) && connectionList.getSelectedIndex()!=-1) {
                    popupMenu.show(e.getComponent(), e.getX(), e.getY());
                }
            }
        });

        // 连接输入框添加 失去焦点 事件  判断是否存在重复连接名称
        connectionName.addFocusListener(new FocusListener() {
            @Override
            public void focusGained(FocusEvent e) {

            }

            @Override
            public void focusLost(FocusEvent e) {
                String text = connectionName.getText();
                if (cache.containsKey(text) && !Objects.equals(connectionList.getSelectedValue(), text)){
                    Messages.showMessageDialog("Connection name exists！", "Warn！", null);
                }
            }
        });

        // test connect 添加点击事件
        connectBtn.addActionListener(e -> {
            String connectionName = this.connectionName.getText();
            DataStore.host = host.getText();
            DataStore.port = port.getText();
            DataStore.username = username.getText();
            DataStore.password = password.getText();
            if (StringUtils.isBlank(connectionName) ||
                    StringUtils.isBlank(host.getText()) ||
                    StringUtils.isBlank(port.getText()) ||
                    StringUtils.isBlank(username.getText()) ||
                    StringUtils.isBlank(password.getText())
            ){
                Messages.showMessageDialog("Connection parameters cannot be empty！", "Warn！", null);
                return;
            }
            if (StringUtils.isNotBlank(connectionList.getSelectedValue()) &&
                    !Objects.equals(connectionList.getSelectedValue(), connectionName) &&
                    cache.containsKey(connectionName)
            ){
                Messages.showMessageDialog("Connection name exists！", "Warn！", null);
                return;
            }
            if (JdbcUtil.testConnection()){
                setDbSelectData(JdbcUtil.allDatabases());
                if (StringUtils.isNotBlank(connectionList.getSelectedValue())){
                    ConnectionBean connectionBean = cache.get(connectionList.getSelectedValue());
                    if (!Objects.equals(connectionBean.getConnectionName(), connectionName) ||
                            !Objects.equals(connectionBean.getHost(), host.getText()) ||
                            !Objects.equals(connectionBean.getPort(), port.getText()) ||
                            !Objects.equals(connectionBean.getUsername(), username.getText()) ||
                            !Objects.equals(connectionBean.getPassword(), password.getText())
                    ){
                        connectionBean.setConnectionName(connectionName);
                        connectionBean.setHost(host.getText());
                        connectionBean.setPort(port.getText());
                        connectionBean.setUsername(username.getText());
                        connectionBean.setPassword(password.getText());
                        cache = connections.stream().collect(Collectors.toMap(ConnectionBean::getConnectionName, Function.identity()));
                        connectionList.setModel(new DefaultComboBoxModel<>(connections.stream().map(ConnectionBean::getConnectionName).toArray(String[]::new)));
                        connectionList.setSelectedValue(connectionName, true);
                    }
                }else {
                    if (cache.containsKey(connectionName)){
                        return;
                    }
                    ConnectionBean connectionBean = new ConnectionBean();
                    connectionBean.setConnectionName(connectionName);
                    connectionBean.setHost(host.getText());
                    connectionBean.setPort(port.getText());
                    connectionBean.setUsername(username.getText());
                    connectionBean.setPassword(password.getText());
                    connections.add(connectionBean);
                    cache.put(connectionName, connectionBean);
                    listModel.addElement(connectionName);
                }
            }else {
                Messages.showMessageDialog("Connection failed, please check database configuration！", "Warn！", null);
            }
        });

        AutoCompleter dbAutoCompleter = new AutoCompleter(dbSelect, "database");

        buttonOK.addActionListener(e -> onOK());

        buttonCancel.addActionListener(e -> onCancel());

        dbSelect.addItemListener(e -> {
            if (e.getStateChange() == ItemEvent.SELECTED){
                JTextField editor = (JTextField) dbSelect.getEditor().getEditorComponent();
                int caretPosition = editor.getCaretPosition();
                if (caretPosition != -1) {
                    try {
                        editor.moveCaretPosition(caretPosition);
                    } catch (IllegalArgumentException ex) {
                        ex.printStackTrace();
                    }
                }
            }
        });

        dbSelect.addActionListener(e -> {
            String database = dbSelect.getSelectedItem().toString();
            if (StringUtils.isNotBlank(database)){
                List<String> tableList = JdbcUtil.allTables(database);
                changeTableSelect(tableList);
            }
        });

        this.tableSearch.getDocument().addDocumentListener(new DocumentListener() {

            private void filterTable(DocumentEvent e){
                Document document = e.getDocument();
                List<String> tables = DataStore.tables;
                if (document.getLength() == 0){
                    changeTableSelect(tables);
                    return;
                }
                try {
                    String text = document.getText(0, document.getLength());
                    List<String> filterTables = tables.stream()
                            .filter(table -> table.contains(text))
                            .collect(Collectors.toList());
                    changeTableSelect(filterTables);
                } catch (BadLocationException badLocationException) {
                    badLocationException.printStackTrace();
                }
            }

            @Override
            public void insertUpdate(DocumentEvent e) {
                filterTable(e);
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                filterTable(e);
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
            }
        });

        this.selectTable.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (1 == e.getClickCount()) {
                    int rowIdx = selectTable.rowAtPoint(e.getPoint());
                    Boolean flag = (Boolean) selectTable.getValueAt(rowIdx, 0);
                    Set<String> tableNames = DataStore.selectedTables;
                    if (null != flag && flag) {
                        tableNames.add(selectTable.getValueAt(rowIdx, 1).toString());
                    } else {
                        tableNames.remove(selectTable.getValueAt(rowIdx, 1).toString());
                    }
                }
            }
        });

        entityFileBtn.addActionListener(e -> {
            JFileChooser fileChooser = new JFileChooser(new File(DataStore.mainPath));
            fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
            fileChooser.showOpenDialog(this);
            File selectedFile = fileChooser.getSelectedFile();
            String filePath = selectedFile.getPath();
            if (!filePath.contains(DataStore.project.getName())){
                return;
            }
            String entityPath = filePath.substring(filePath.indexOf("java") + 5).replace(File.separator, ".");
            entityPackage.setText(entityPath);
        });

        daoFileBtn.addActionListener(e -> {
            JFileChooser fileChooser = new JFileChooser(new File(DataStore.mainPath));
            fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
            fileChooser.showOpenDialog(this);
            File selectedFile = fileChooser.getSelectedFile();
            String filePath = selectedFile.getPath();
            if (!filePath.contains(DataStore.project.getName())){
                return;
            }
            String daoPath = filePath.substring(filePath.indexOf("java") + 5).replace(File.separator, ".");
            daoPackage.setText(daoPath);
        });

        mapperFileBtn.addActionListener(e -> {
            JFileChooser fileChooser = new JFileChooser(new File(DataStore.mainPath));
            fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
            fileChooser.showOpenDialog(this);
            File selectedFile = fileChooser.getSelectedFile();
            String filePath = selectedFile.getPath();
            if (!filePath.contains(DataStore.project.getName())){
                return;
            }
            String mapperPath = filePath.substring(filePath.indexOf("resources") + 10).replace(File.separator, ".");
            xmlPackage.setText(mapperPath);
        });

        // call onCancel() when cross is clicked
        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                onCancel();
            }
        });

        // call onCancel() on ESCAPE
        contentPane.registerKeyboardAction(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                onCancel();
            }
        }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
    }

    private void clearForm() {
        connectionName.setText("");
        host.setText("");
        port.setText("");
        username.setText("");
        password.setText("");
    }

    private void setConnection(ConnectionBean connection){
        connectionName.setText(connection.getConnectionName());
        host.setText(connection.getHost());
        port.setText(connection.getPort());
        username.setText(connection.getUsername());
        password.setText(connection.getPassword());
        entityPackage.setText(connection.getEntityPackage());
        daoPackage.setText(connection.getDaoPackage());
        xmlPackage.setText(connection.getXmlPackage());
        tablePrefix.setText(connection.getTablePrefix());
    }

    private void onOK() {
        // add your code here
        String javaPath = DataStore.mainPath + File.separator + "java";
        String resourcesPath = DataStore.mainPath + File.separator + "resources";
        if (dbSelect.getSelectedItem() == null){
            Messages.showMessageDialog("Database cannot be empty", "Warn!", null);
            return;
        }
        String dbName = dbSelect.getSelectedItem().toString();
        String entityPackageText = entityPackage.getText();
        String daoPackageText = daoPackage.getText();
        String mapperPackageText = xmlPackage.getText();
        String tablePrefixText = tablePrefix.getText();
        Set<String> tables = DataStore.selectedTables;
        boolean lombokSelected = lombokRadio.isSelected();
        boolean mergeSupportSelected = mergeRadio.isSelected();
        boolean tinyintCastIntegerSelected = tinyintCastInteger.isSelected();
        if (StringUtils.isBlank(dbName) ||
                CollectionUtils.isEmpty(tables) ||
                StringUtils.isBlank(entityPackageText) ||
                StringUtils.isBlank(daoPackageText) ||
                StringUtils.isBlank(mapperPackageText)
        ){
            Messages.showMessageDialog("Database, table, and code package path cannot be empty", "Warn!", null);
            return;
        }
        MyBatisGeneratorConfig generatorConfig = MyBatisGeneratorConfig.builder()
                .database(dbName)
                .tableNames(new ArrayList<>(tables))
                .entityPackage(entityPackageText)
                .daoPackage(daoPackageText)
                .mapperPackage(mapperPackageText)
                .javaPath(javaPath)
                .resourcesPath(resourcesPath)
                .lombok(lombokSelected)
                .mergeSupport(mergeSupportSelected)
                .tablePrefix(tablePrefixText)
                .tinyintCastInteger(tinyintCastIntegerSelected)
                .build();
        GeneratorService.getInstance().generator(generatorConfig);
        String selectedValue = connectionList.getSelectedValue();
        ConnectionBean connectionBean = cache.get(selectedValue);
        if (connectionBean != null){
            connectionBean.setEntityPackage(entityPackageText);
            connectionBean.setDaoPackage(daoPackageText);
            connectionBean.setXmlPackage(mapperPackageText);
            connectionBean.setTablePrefix(tablePrefixText);
        }
        File file = new File(DataStore.mainPath);
        VirtualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file);
        virtualFile.refresh(false, true);
        DataStore.selectedTables.clear();
        dispose();
    }

    private void onCancel() {
        // add your code here if necessary
        dispose();
    }

    public void setDbSelectData(List<String> databases){
        dbSelect.setModel(new DefaultComboBoxModel<>(databases.toArray(new String[0])));
        DataStore.databases = databases;
    }

    /**
     * 根据table列表改变JTable
     * @param tableList
     */
    private void changeTableSelect(List<String> tableList){
        String[] title = {"", "表名"};
        Object[][] data = new Object[tableList.size()][2];
        for (int i = 0; i < tableList.size(); i++) {
            data[i][1] = tableList.get(i);
        }

        selectTable.setModel(new DefaultTableModel(data, title));
        TableColumn tc = selectTable.getColumnModel().getColumn(0);
        tc.setCellEditor(new DefaultCellEditor(new JCheckBox()));
        tc.setCellEditor(selectTable.getDefaultEditor(Boolean.class));
        tc.setCellRenderer(selectTable.getDefaultRenderer(Boolean.class));
        tc.setMaxWidth(100);

        int rowCount = selectTable.getRowCount();
        for (int i = 0; i < rowCount; i++) {
            if (DataStore.selectedTables.contains(selectTable.getValueAt(i,1).toString())){
                selectTable.setValueAt(true, i, 0);
            }
        }
    }
}
